﻿using UnityEngine;
using System.Collections;
using System.Threading;
using System;
using System.IO;
using UnityEditor;

public class NeuralNetGPU
{
    public int count = 0;
    public string name = "iRobot";
    public int saveTime = 5;
    public bool saving = false;
    public int inputNum = 256, inputHeight = 256;
    public int outputNum = 256, outputHeight = 256;
    public int hideLayNum = 30;
    public int hideWidth = 256, hideHeight = 256;
    public int hideLinLen = 8;
    public int hideLinNum = 3;
    public int threadNum = 5;
    public float inputDecrate = 0.333f, powerIncRate = 0.01f, powerDecRate = 0.01f;
    public float layerIncRate = 0.0001f, layerDecRate = 0.0001f;
    public bool seatchLink = false;
    public bool initing = false;

    public float[] inlay;
    private float[] outlay;

    private Neural[] neurals;

    public ComputeShader computeShader;

    public bool runing;

    public delegate void EventHandlerUpdate();

    public EventHandlerUpdate run;
    public int sleep = 8, updateSpeed = 125;//8=120帧/秒；  16=60帧每秒
    private System.Random random = new System.Random();

    private static string persistentDataPath = ".";

    public struct NeuralLink
    {
        public int linkX;
        public int linkY;
        public int linkZ;
        public float power;
    }
    public struct Neural
    {
        public float input;
        public float layer;

        public NeuralLink[] links;

    }
    public NeuralNetGPU()
    {

    }
    public NeuralNetGPU(string name)
    {
        this.name = name;
    }

    public void Init()
    {
        initing = true;
        //inlay = new float[inputNum*inputHeight];
        //outlay = new float[outputNum * outputHeight];
        //input = new float[inputNum * inputHeight + hideLayNum * hideWidth * hideHeight + outputNum * outputHeight];
        neurals = new Neural[inputNum * inputHeight + hideLayNum * hideWidth * hideHeight + outputNum * outputHeight];

        for (int i = 0; i < inputNum; i++)
        {
            for (int j = 0; j < inputHeight; j++)
            {
                int idx = Index(0, i, j);
                neurals[idx].links = new NeuralLink[hideLinNum];
                for (int k = 0; k < hideLinNum; k++)
                {
                    int si = (int)Math.Round(1f * hideWidth * i / inputNum);
                    int sj = (int)Math.Round(1f * hideHeight * j / inputHeight);
                    neurals[idx].links[k].linkX = 1;
                    neurals[idx].links[k].linkY = (short)random.Next(Math.Max(0, si - hideLinLen), Math.Min(si + hideLinLen + 1, hideWidth));
                    neurals[idx].links[k].linkZ = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
                    neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;
                }
            }
        }
        // hide layer
        hideLinNum = Mathf.Min(8, hideLinNum);
        for (int i = 1; i <= hideLayNum; i++)
        {
            count = i;
            for (int j = 0; j < hideWidth; j++)
            {
                for (int z = 0; z < hideHeight; z++)
                {
                    int idx = Index(i, j, z);
                    neurals[idx].links = new NeuralLink[hideLinNum];
                    for (int k = 0; k < hideLinNum; k++)
                    {
                        neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f; ;
                        //linkX[i][j][k] = i < hideLayNum ? UnityEngine.Random.Range(1, hideLayNum + 1) : hideLayNum + 1;
                        //linkY[i][j][k] = UnityEngine.Random.Range(-1, (i < hideLayNum?hideWidth:outputNum));
                        if (i == hideLayNum)
                        {
                            int sj = (int)Math.Round(1f * j / hideWidth * outputNum);
                            int sz = (int)Math.Round(1f * z / hideHeight * outputHeight);
                            neurals[idx].links[k].linkX = (short)(hideLayNum + 1);
                            neurals[idx].links[k].linkY = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
                            neurals[idx].links[k].linkZ = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
                            //Debug.Log("HideLayEndLayLink:" +j+"@"+ k + "  ->  " + linkY[i][j][k]);
                        }
                        else
                        {
                            neurals[idx].links[k].linkX = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
                            neurals[idx].links[k].linkY = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, hideWidth));
                            neurals[idx].links[k].linkZ = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
                        }

                    }
                }
            }
        }

        // output layer
        for (int j = 0; j < outputNum; j++)
        {
            for (int z = 0; z < outputHeight; z++)
            {
                int idx = Index(hideLayNum + 1, j, z);
                neurals[idx].links = new NeuralLink[hideLinNum];
            }
        }
        //Debug.Log("NN Init End");
        initing = false;
    }

    public void InitInput()
    {
        inlay = new float[inputNum * inputHeight];
        outlay = new float[outputNum * outputHeight];

        neuralData = new NeuralData[neurals.Length];
        for (int i = 0; i < neuralData.Length; i++)
        {
            neuralData[i].layer = neurals[i].layer;
            neuralData[i].link1 = neurals[i].links[0];
            neuralData[i].link2 = neurals[i].links[1];
            neuralData[i].link3 = neurals[i].links[2];
        }
    }

    public int inwidth()
    {
        return inputNum;
    }

    public int outwidth()
    {
        return outputNum;
    }

    public void inputValue(int i, int z, short val)
    {
        int idx = Index(0, i, z);
        inlay[idx] = val;
        
    }
    //public void inputValue(int i, int j, int z, short val)
    //{
    //    if (val != 0)
    //    {
    //        if (i == 0)
    //        {
    //            inputValue(j, z, val);
    //        }
    //        else
    //        {
    //            input[Index(i, j, z)] += val;
    //        }
    //    }
    //}
    public short getOutInputLayer(int j, int z)
    {
        int i = j * outputHeight + z;
        return (short)(outlay[i]);
    }

    private int Index(int i, int j, int z)
    {
        if (i == 0)
        {
            return j * inputHeight + z;
        }
        else if (i == hideLayNum + 1)
        {
            return inputNum * inputHeight + hideLayNum * hideWidth * hideHeight + j * outputHeight + z;
        }
        else
        {
            return inputNum * inputHeight + (i - 1) * hideWidth * hideHeight + j * hideHeight + z;
        }
    }

    //private int IndexLink(int i, int j, int z, int k)
    //{
    //    return Index(i, j, z) * hideLinNum + k;
    //}

    public short getInputLayer(int i, int j, int z)
    {
        int idx = Index(i, j, z);
        return (short)(neurals[idx].input + neurals[idx].layer);
    }

    public bool checkOut(int i, int z)
    {
        int idx = i*outputHeight+z;
        return outlay[idx]> 0;

    }
    public bool check(int i, int j, int z)
    {
        int idx = Index(i, j, z);
        return (neurals[idx].input + neurals[idx].layer) > 0;

    }

    //public void clear(int i)
    //{
    //    if (input != null)
    //    {
    //        input[hideLayNum + 1][i] = 0;
    //    }
    //}

    // Use this for initialization
    public virtual void Start()
    {
        runing = true;

        for (short i = 0; i < threadNum; i++)
        {
            short tid = i;
            Debug.Log("ThreadUpdateStart:" + tid);

            Thread t = new Thread(() =>
            {
                Thread.Sleep(10000);
                for (long n = 0; runing; n++)
                {
                    ThreadUpdate(tid, n);
                    Thread.Sleep(sleep);
                }
                Debug.Log("ThreadUpdateEnd:" + tid);
            });
            t.IsBackground = true;
            t.Start();  //其实用的也是ThreadStart
        }
        
    }
    public ComputeBuffer inlays;
    private ComputeBuffer outlays;

    private ComputeBuffer neuralsBuffer;
    public void SetGetInOutLayDate()
    {
        if (inlays != null)
        {
            inlays.SetData(inlay);
            //computeShader.SetInt("runing", runing ? 1 : 0);
            computeShader.SetBuffer(kernelIndex, "inlayBuffer", inlays);
            //启动计算Shader
            computeShader.Dispatch(kernelIndex, hideWidth / 16, hideHeight / 16, (hideLayNum + 2) / 4);

            for (int i = 0; i < inlay.Length; i++)
            {
                inlay[i] = 0;
            }
        }
        GetOutputLayDate();
    }

    public void GetOutputLayDate()
    {
        if (outlays != null)
        {
            outlays.GetData(outlay);

            if (saving)
            {

                neuralsBuffer.GetData(neuralData);

                //if (name.Equals("iRobot") || name.Equals("iRobotSL") || name.Equals("iRobotL"))
                //{
                //    int outIdx = Index(hideLayNum + 1, 0, 0);
                //    for (int i = 0; i < neuralData.Length; i++)
                //    {
                //        if (i >= outIdx)
                //        {
                //            if (neuralData[i].input != 0 || neuralData[i].layer != 0)
                //            {
                //                string linkStr = "out lay";
                //                if (i < outIdx)
                //                {
                //                    linkStr = neuralData[i].link1.linkX + "," + neuralData[i].link1.linkY + "," + neuralData[i].link1.linkZ + ":" + neuralData[i].link1.power
                //                        + "   " + neuralData[i].link2.linkX + "," + neuralData[i].link2.linkY + "," + neuralData[i].link2.linkZ + ":" + neuralData[i].link2.power
                //                        + "   " + neuralData[i].link3.linkX + "," + neuralData[i].link3.linkY + "," + neuralData[i].link3.linkZ + ":" + neuralData[i].link3.power;
                //                }
                //                Debug.Log(name + " Neural: " + neuralData[i].input + "/" + neuralData[i].layer + " link:\n" + linkStr);
                //            }
                //        }
                //    }
                //}
                //for (int i = 0; i < outlay.Length; i++)
                //{
                //    if (outlay[i] != 0)
                //    {
                //        Debug.Log("OutData: [" + i + "]" + outlay[i]);
                //        //neuralData[i].input + "/" + neuralData[i].layer + " link:" + neuralData[i].link1.power + " ," + neuralData[i].link2.power + " ," + neuralData[i].link3.power);
                //    }
                //}
            }
        }
    }

    private NeuralData[] neuralData; 
    public struct NeuralData
    {
        public float input;
        public float layer;
        public NeuralLink link1;
        public NeuralLink link2;
        public NeuralLink link3;
    }

    void ThreadUpdate(short tid, long count)
    {
        short n = (short)(hideLayNum / threadNum), k = 0;
        int destX = 0, destY = 0, destZ = 0;
        float tvn = 0, v = 0, tv = 0;

        if (tid == 0)
        {
            // input layer
            for (int i = 0; i < inputNum; i++)
            {
                for (int j = 0; j < inputHeight; j++)
                {
                    int idx = Index(0, i, j);
                    v = (short)(neurals[idx].input + neurals[idx].layer);
                    if (v > 0)
                    {
                        for (k = 0; k < hideLinNum; k++)
                        {
                            destY = neurals[idx].links[k].linkY;
                            destZ = neurals[idx].links[k].linkZ;
                            if (destY < 0)
                            {
                                //搜索可连接节点
                                int si = (int)Math.Round(1f * hideWidth * i / inputNum);
                                int sj = (int)Math.Round(1f * hideHeight * j / inputHeight);
                                if (seatchLink)
                                {
                                    for (int w = Math.Max(0, si - hideLinLen); destY == -1 && w < Math.Min(si + hideLinLen + 1, hideWidth); w++)
                                    {
                                        for (int h = Math.Max(0, sj - hideLinLen); h < Math.Min(sj + hideLinLen + 1, hideHeight); h++)
                                        {
                                            if (neurals[idx].input + neurals[idx].layer >= 0)
                                            {
                                                neurals[idx].links[k].linkX = 1;
                                                neurals[idx].links[k].linkY = destY = (short)w;
                                                neurals[idx].links[k].linkZ = destZ = (short)h;
                                                neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                                break;
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    neurals[idx].links[k].linkY = destY = (short)random.Next(Math.Max(0, si - hideLinLen), Math.Min(si + hideLinLen + 1, hideWidth));
                                    neurals[idx].links[k].linkZ = destZ = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, hideHeight));
                                    neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[0][i][j] > layer[1][offsetY][offsetZ] ? 1f : -1f);
                                }
                                if (destY < 0)
                                {
                                    continue;
                                }
                            }

                            //tv = (float)Math.Round(10 * v * linkPower[lnkIdx]);
                            //int dIdx = Index(1, destY, destZ);
                            //tvn = tv + input[dIdx];


                            //if (tvn > short.MaxValue)
                            //{
                            //    tvn = short.MaxValue;
                            //}
                            //else if (tvn < short.MinValue)
                            //{
                            //    tvn = short.MinValue;
                            //}

                            //input[dIdx] = tvn;

                            //if (float.IsNaN(input[1][offsetY]))
                            //{
                            //    input[1][offsetY] = 0;
                            //}
                            //Debug.Log("RootThreadUpdate Input "+i+","+j+" - "+k+": "+ power[0][i][j][k] +"*" + v+"  "+ input[1][offsetY][offsetZ] + "/"+ layer[1][offsetY][offsetZ]);

                            //if (neurals[idx].links[k].power == 0)
                            //{
                            //    neurals[idx].links[k].linkY = -1;
                            //}
                            //if (float.IsNaN(neurals[idx].links[k].power))
                            //{
                            //    neurals[idx].links[k].power = powerIncRate;
                            //}

                        }
                    }
                }
                //Debug.Log("RootThreadUpdate " + i + ":" + input[0][i] +"/"+ layer[0][i]);
                //if (float.IsInfinity(input[0][i]))
                //{
                //    input[0][i] = 0;
                //}
                //if (float.IsNegativeInfinity(layer[0][i]))
                //{
                //    layer[0][i] = 0;
                //}
                //if (float.IsNaN(layer[0][i]))
                //{
                //    layer[0][i] = 0;
                //}
            }
        }
        //Debug.Log("ThreadUpdate:" + ti);

        // hide layer  0111/111/111/111
        for (short i = (short)(1 + (n * tid)); i <= ((tid + ((tid + 1 == threadNum) ? 2 : 1)) * n) && i <= hideLayNum; i++)
        {
            //Debug.Log("ThreadUpdate:"+ti+"-" + i);
            for (short j = 0; j < hideWidth; j++)
            {
                for (int z = 0; z < hideHeight; z++)
                {
                    int idx = Index(i, j, z);
                    v = (short)(neurals[idx].input + neurals[idx].layer);
                    if (v > 0)
                    {
                        //Debug.Log("NNUpdateHideLay: "+i+","+j+": "+v);
                        for (k = 0; k < hideLinNum; k++)
                        {
                            destX = neurals[idx].links[k].linkX;
                            destY = neurals[idx].links[k].linkY;
                            destZ = neurals[idx].links[k].linkZ;
                            if (destX == -1)
                            {
                                if (i > 0 && i < hideLayNum)
                                {
                                    if (seatchLink)
                                    {
                                        //雷达式搜索可连接节点
                                        for (short l = 1; l <= hideLinLen && destX == -1; l++)
                                        {
                                            // 圈右
                                            if (i + l <= hideLayNum)
                                            {
                                                for (short m = (short)-l; m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + l), y = (short)(j + m);
                                                    if (y < 0) continue;
                                                    if (y >= hideWidth) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //圈底
                                            if (j + l < hideWidth)
                                            {
                                                for (short m = l; m > -l && destX == -1; m--)
                                                {
                                                    short x = (short)(i + m), y = (short)(j + l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum || y >= hideWidth) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //圈左
                                            if (i - l > 0)
                                            {
                                                for (short m = l; m > -l && destX == -1; m--)
                                                {
                                                    short x = (short)(i - l), y = (short)(j + m);
                                                    if (y < 0) continue;
                                                    if (y >= hideWidth) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            // 圈顶
                                            if (j - l >= 0)
                                            {
                                                for (short m = (short)-l; m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), y = (short)(j - l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum || y >= hideWidth) break;
                                                    for (int sz = Math.Max(0, z - l); sz <= Math.Min(hideHeight - 1, z + l); sz++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //顶层
                                            if (z + l < hideHeight)
                                            {
                                                for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), sz = (short)(z + l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum) break;
                                                    for (int y = Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                            //底层
                                            if (z - l >= 0)
                                            {
                                                for (short m = (short)(-l + 1); m < l && destX == -1; m++)
                                                {
                                                    short x = (short)(i + m), sz = (short)(z - l);
                                                    if (x <= 0) continue;
                                                    if (x > hideLayNum) break;
                                                    for (int y = Math.Max(0, j - l + 1); y <= Math.Min(hideWidth - 1, j + l - 1); y++)
                                                    {
                                                        int sIdx = Index(x, y, sz);
                                                        if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                        {
                                                            neurals[idx].links[k].linkX = destX = x;
                                                            neurals[idx].links[k].linkY = destY = y;
                                                            neurals[idx].links[k].linkZ = destZ = sz;
                                                            neurals[idx].links[k].power *= 100;// powerRate * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        neurals[idx].links[k].linkX = destX = (short)random.Next(Mathf.Max(1, i - hideLinLen), Mathf.Min(i + hideLinLen + 1, hideLayNum + 1));
                                        neurals[idx].links[k].linkY = destY = (short)random.Next(Mathf.Max(0, j - hideLinLen), Mathf.Min(j + hideLinLen + 1, hideWidth));
                                        neurals[idx].links[k].linkZ = destZ = (short)random.Next(Math.Max(0, z - hideLinLen), Math.Min(z + hideLinLen + 1, hideHeight));
                                        neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][offsetZ] ? 1f : -1f);
                                    }
                                }
                                else
                                {
                                    int sj = (int)Math.Round((1f * j / hideWidth) * outputNum);
                                    int sz = (int)Math.Round((1f * z / hideHeight) * outputHeight);
                                    if (seatchLink)
                                    {
                                        for (int m = Math.Max(0, sj - hideLinLen); destX == -1 && m < Math.Min(sj + hideLinLen + 1, outputNum); m++)
                                        {
                                            short x = (short)(hideLayNum + 1), y = (short)m;
                                            for (int h = Math.Max(0, sz - hideLinLen); h < Math.Min(sz + hideLinLen + 1, outputHeight); h++)
                                            {
                                                int sIdx = Index(x, y, h);
                                                if (neurals[sIdx].input + neurals[sIdx].layer >= 0)
                                                {
                                                    neurals[idx].links[k].linkX = destX = x;
                                                    neurals[idx].links[k].linkY = destY = y;
                                                    neurals[idx].links[k].linkZ = destZ = (short)h;
                                                    neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        neurals[idx].links[k].linkX = destX = (short)(hideLayNum + 1);
                                        neurals[idx].links[k].linkY = destY = (short)random.Next(Math.Max(0, sj - hideLinLen), Math.Min(sj + hideLinLen + 1, outputNum));
                                        neurals[idx].links[k].linkZ = destZ = (short)random.Next(Math.Max(0, sz - hideLinLen), Math.Min(sz + hideLinLen + 1, outputHeight));
                                        neurals[idx].links[k].power = random.Next(-9999, 10000) / 10000f;// * (layer[i][j][z] > layer[offsetX][offsetY][sz] ? 1f : -1f);
                                    }
                                }
                                if (destY < 0)
                                {
                                    continue;//搜索完后处理下一个链接
                                }
                            }

                        }

                    }

                }
                //if (float.IsInfinity(input[i][j]))
                //{
                //    input[i][j] = 0;
                //}
                //if (float.IsNegativeInfinity(layer[i][j]))
                //{
                //    layer[i][j] = 0;
                //}
                //if (float.IsNaN(layer[i][j]))
                //{
                //    layer[i][j] = 0;
                //}
            }
        }

        // output layer
        if (tid == threadNum - 1)
        {
            run();
            for (int i = 0; i < outlay.Length; i++)
            {
                outlay[i] = 0;
            }
        }
    }

    public void Update()
    {
        if (inlays != null)
        {
            SetGetInOutLayDate();
        }
        
    }

    public void OnDestroy()
    {
        
        neurals=null;
        
        inlays.Release();
        outlays.Release();

        neuralsBuffer.Release();
    }
    public void OnApplicationQuit()
    {
        runing = false;
        //computeShader.SetInt("runing", runing ? 1 : 0);
        Debug.Log("NN: OnApplicationQuit");
    }

    internal void OnSerialize(BinaryWriter bfs)
    {
        bfs.Write((short)inputNum);
        bfs.Write((short)inputHeight);
        bfs.Write((short)outputNum);
        bfs.Write((short)outputHeight);
        bfs.Write((short)hideLayNum);
        bfs.Write((short)hideWidth);
        bfs.Write((short)hideHeight);
        bfs.Write((short)hideLinLen);
        bfs.Write((short)hideLinNum);
        bfs.Write((short)threadNum);
        bfs.Write(inputDecrate);
        bfs.Write(powerIncRate);
        bfs.Write(powerDecRate);
        bfs.Write(layerIncRate);
        bfs.Write(layerDecRate);
        bfs.Write((short)sleep);
        bfs.Write((short)updateSpeed);
        int length = hideLayNum + 2;
        bfs.Write((short)length);
        for (int i = 0; i < length; i++)
        {
            if (i == 0)
            {
                bfs.Write((short)inputNum);
                bfs.Write((short)inputHeight);
                for (int j = 0; j < inputNum; j++)
                {
                    for (int z = 0; z < inputHeight; z++)
                    {
                        int idx = Index(i, j, z);
                        bfs.Write((short)neurals[idx].layer);
                        for (int k = 0; k < hideLinNum; k++)
                        {
                            bfs.Write((short)neurals[idx].links[k].linkX);
                            bfs.Write((short)neurals[idx].links[k].linkY);
                            bfs.Write((short)neurals[idx].links[k].linkZ);
                            bfs.Write(neurals[idx].links[k].power);
                        }
                    }
                }
            }
            else
            if (i == hideLayNum + 1)
            {
                bfs.Write((short)outputNum);
                bfs.Write((short)outputHeight);
                for (int j = 0; j < outputNum; j++)
                {
                    for (int z = 0; z < outputHeight; z++)
                    {
                        bfs.Write((short)neurals[Index(i, j, z)].layer);
                    }
                }
            }
            else
            {
                bfs.Write((short)hideWidth);
                bfs.Write((short)hideHeight);
                for (int j = 0; j < hideWidth; j++)
                {
                    for (int z = 0; z < hideHeight; z++)
                    {
                        int idx = Index(i, j, z);
                        bfs.Write((short)neurals[idx].layer);
                        for (int k = 0; k < hideLinNum; k++)
                        {
                            bfs.Write((short)neurals[idx].links[k].linkX);
                            bfs.Write((short)neurals[idx].links[k].linkY);
                            bfs.Write((short)neurals[idx].links[k].linkZ);
                            bfs.Write(neurals[idx].links[k].power);
                        }
                    }
                }
            }
        }

    }

    internal void OnUnSerialize(BinaryReader bfs)
    {
        inputNum = bfs.ReadInt16();
        inputHeight = bfs.ReadInt16();
        outputNum = bfs.ReadInt16();
        outputHeight = bfs.ReadInt16();
        hideLayNum = bfs.ReadInt16();
        hideWidth = bfs.ReadInt16();
        hideHeight = bfs.ReadInt16();
        hideLinLen = bfs.ReadInt16();
        hideLinNum = bfs.ReadInt16();
        threadNum = bfs.ReadInt16();
        inputDecrate = bfs.ReadSingle();
        powerIncRate = bfs.ReadSingle();
        powerDecRate = bfs.ReadSingle();
        layerIncRate = bfs.ReadSingle();
        layerDecRate = bfs.ReadSingle();
        sleep = bfs.ReadInt16();
        updateSpeed = bfs.ReadInt16();
        int length = bfs.ReadInt16();
        neurals = new Neural[inputNum * inputHeight + hideLayNum * hideWidth * hideHeight + outputNum * outputHeight];
        
        for (int i = 0; i < length; i++)
        {
            count = i;
            int w = bfs.ReadInt16();
            int h = bfs.ReadInt16();

            for (int j = 0; j < w; j++)
            {
                for (int z = 0; z < h; z++)
                {
                    int idx = Index(i, j, z);

                    neurals[idx].layer = bfs.ReadInt16();
                    neurals[idx].links = new NeuralLink[hideLinNum];
                    if (i < length - 1)
                    {
                        for (int k = 0; k < hideLinNum; k++)
                        {
                            neurals[idx].links[k].linkX = bfs.ReadInt16();
                            neurals[idx].links[k].linkY = bfs.ReadInt16();
                            neurals[idx].links[k].linkZ = bfs.ReadInt16();
                            neurals[idx].links[k].power = bfs.ReadSingle();
                        }
                    }
                }
            }
        }
    }

    internal void StartLoad()
    {
        persistentDataPath = Application.persistentDataPath;

        loadData++;

        Thread rthread = new Thread(() =>
        {
            Debug.Log("LoadDataStart");

            LoadData();

            while (loadData != 0)
            {
                Thread.Sleep(1000);
            }
            Start();
            
            Debug.Log("LoadDataEnd");


            // 定时存档
            while (runing)
            {
                Thread.Sleep(saveTime * 60000);
                
                if (runing)
                {
                    Debug.Log("SaveDataStart");
                    SaveData();
                    Debug.Log("SaveDataEnd");
                }
            }
        });
        rthread.IsBackground = true;
        rthread.Start();


    }

    public void StartComputeShader()
    {
        if (loadData==0 && inlays == null)
        {
            // 创建用于存储计算结果的缓冲区
            inlays = new ComputeBuffer(inlay.Length, sizeof(float));
            outlays = new ComputeBuffer(outlay.Length, sizeof(float));
            
            neuralsBuffer = new ComputeBuffer(neurals.Length, 8+16*3);
            neuralsBuffer.SetData(neuralData);

            // 设置Compute Shader的参数
            kernelIndex = computeShader.FindKernel(name);

            //computeShader.SetInt("runing", runing ? 1 : 0);
            computeShader.SetInt("inputNum", inputNum);
            computeShader.SetInt("inputHeight", inputHeight);
            computeShader.SetInt("outputNum", outputNum);
            computeShader.SetInt("outputHeight", outputHeight);
            computeShader.SetInt("laylen", hideLayNum + 2);
            computeShader.SetInt("width", hideWidth);
            computeShader.SetInt("height", hideHeight);
            computeShader.SetInt("linkLen", hideLinLen);
            computeShader.SetInt("linkNum", hideLinNum);
            computeShader.SetFloat("inputDecRate", inputDecrate);
            computeShader.SetFloat("layerIncRate", layerIncRate);
            computeShader.SetFloat("layerDecRate", layerDecRate);
            computeShader.SetFloat("powerIncRate", powerIncRate);
            computeShader.SetFloat("powerDecRate", powerDecRate);

            computeShader.SetBuffer(kernelIndex, "inlayBuffer", inlays);
            computeShader.SetBuffer(kernelIndex, "outlayBuffer", outlays);

            computeShader.SetBuffer(kernelIndex, "neuralsBuffer", neuralsBuffer);

            //启动计算Shader
            computeShader.Dispatch(kernelIndex, hideWidth / 16, hideHeight / 16, (hideLayNum+2)/4);
        }
    }

    public static int loadData = 0;
    private int kernelIndex;

    private bool LoadData()
    {
        bool suc = false;
        if (suc = BinaryDeserialize(name + ".dat"))
        {
            if (neurals == null)
            {
                Debug.Log("Load " + name + " ReInit");
                Init();
            }
            
            //if (nn.inputNum != neuralNet.inputNum || nn.outputNum != neuralNet.outputNum || nn.hideLayNum != neuralNet.hideLayNum || nn.hideWidth != neuralNet.hideWidth || nn.hideLinNum != neuralNet.hideLinNum)
            //{
            //    neuralNet = nn;
            //    Debug.Log("Load " + neuralNet.name + " Data version error");
            //    //neuralNet.Init();
            //}
            //else
            //{
            //    Debug.Log("Load " + neuralNet.name + " success");
            //    neuralNet = nn;
            //}
        }
        else
        {
            Init();
        }


        InitInput();

        lock (this)
        {
            loadData--;
        }
        return suc;
    }
    public void SaveData()
    {
        if (saving) return;

        saving = true;
        Thread.Sleep(10000);
        for (int i = 0; i < neuralData.Length; i++)
        {
            neurals[i].layer = neuralData[i].layer;
            neurals[i].links[0] = neuralData[i].link1;
            neurals[i].links[1] = neuralData[i].link2;
            neurals[i].links[2] = neuralData[i].link3;
        }
        bool suc = BinarySerialize(name + ".dat");
        Debug.Log("Save " + name + ".dat " + suc);

        saving = false;
    }

    /// <summary>
    /// 序列化二进制
    /// </summary>
    public bool BinarySerialize(string outputPath)
    {
        //if (!File.Exists(outputPath))
        //{
        //    Debug.LogError($"序列化二进制失败，输出路径有误，filePath：{outputPath}");
        //    return false;
        //}
        try
        {
            String tmpPath = persistentDataPath + "/" + outputPath + ".tmp";
            Debug.Log("Save path: " + tmpPath);
            string dirPath = Path.GetDirectoryName(tmpPath);
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            using (FileStream fs = new FileStream(tmpPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //binaryFormatter.Serialize(fs, obj);
                BufferedStream bfs = new BufferedStream(fs);
                OnSerialize(new BinaryWriter(bfs));
                bfs.Flush();
                bfs.Close();
            }
            File.Delete(persistentDataPath + "/" + outputPath);
            File.Move(tmpPath, persistentDataPath + "/" + outputPath);

        }
        catch (Exception e)
        {
            Debug.LogError("序列化二进制失败，" + e);
            return false;
        }
        return true;
    }
    /// <summary>
    /// 反序列化二进制
    /// </summary>
    public bool BinaryDeserialize(string binaryFilePath)
    {
        binaryFilePath = persistentDataPath + "/" + binaryFilePath;
        Debug.Log("Load Path: " + binaryFilePath);
        if (!File.Exists(binaryFilePath))
        {
            Debug.LogWarning("反序列化二进制失败，找不到二进制文件：" + binaryFilePath);
            return false;
        }
        try
        {
            using (FileStream fs = File.OpenRead(binaryFilePath))
            {
                //BinaryFormatter binaryFormatter = new BinaryFormatter();
                //obj = (T)binaryFormatter.Deserialize(fs);
                BufferedStream bfs = new BufferedStream(fs);
                OnUnSerialize(new BinaryReader(bfs));
                bfs.Close();
            }
        }
        catch (Exception e)
        {
            Debug.LogError("反序列化二进制失败，" + e);
            return false;
        }
        return true;
    }

    
}
